package tij4.concurrent.source;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/*
    对象池:管理着数量有限的对象,当要使用对象时可以签出它们,
    在用户使用完毕后,可以将它们签回.
 */
public class Pool<T> {
    private int size;
    private List<T> itemList = new ArrayList<>();
    private volatile boolean[] checkedOut;
    private Semaphore semaphore;

    /*
        本类初始化的时候会:
            size赋值
            创建size大小的boolean数组checkOut
            创建拥有25个许可证,按照FIFO顺序授予许可的semaphore
            并像itemList中填充size个classObject
     */
    public Pool(Class<T> classObject, int size) {
        this.size = size;
        checkedOut = new boolean[size];
        semaphore = new Semaphore(size, true);  //初始化25个许可证,是按照FIFO的顺序授予许可
        for (int i = 0; i < size; i++) {
            try {
                itemList.add(classObject.newInstance());
            }catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    public T checkOut() throws InterruptedException {
        semaphore.acquire();    //若没有许可则阻塞
        return getItem();
    }
    //只有要签入的元素在itemList中才会将其签入
    public void checkIn(T x) {
        if(releaseItem(x)) {
            semaphore.release();    //释放许可证
        }
    }
    //优先提供itemList靠前未签出的对象
    private synchronized T getItem() {
        for (int i = 0; i < size; ++i) {
            if (!checkedOut[i]) {
                checkedOut[i] = true;
                return itemList.get(i); //返回循环到的第一个是false
            }
        }
        return null;
    }

    /*
        在itemList中查询item是否存在,通过下标表示
        如果下标为 -1 则表示itemList中没有此item 并返回false
        如果checkOut中对应的item的下标为true则
            checkOut对应的那个元素变为false表示元素未签出
            返回true
        如果下标不等于 -1 checkOut中对应的元素未false(未签出)返回false

     */
    private synchronized boolean releaseItem(T item) {
        int index = itemList.indexOf(item);
        if (index == -1) {
            return false;
        }
        if (checkedOut[index]) {
            checkedOut[index] = false;
            return true;
        }
        return false;
    }
}
